package com.naiterui.ehp.bs.b2c.inventory.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bs.b2c.common.bo.inventory.PlatformStockBO;
import com.naiterui.ehp.bs.b2c.common.bo.inventory.StockBO;
import com.naiterui.ehp.bs.b2c.common.bo.inventory.StockQuantityBO;
import com.naiterui.ehp.bs.b2c.common.bo.product.SkuBO;
import com.naiterui.ehp.bs.b2c.constants.MallConstants;
import com.naiterui.ehp.bs.b2c.inventory.entity.Inventory;
import com.naiterui.ehp.bs.b2c.inventory.entity.InventoryLog;
import com.naiterui.ehp.bs.b2c.inventory.exception.StockExceptionCodes;
import com.naiterui.ehp.bs.b2c.inventory.exception.StockServiceException;
import com.naiterui.ehp.bs.b2c.inventory.repository.IStockLogRepository;
import com.naiterui.ehp.bs.b2c.inventory.repository.IStockRepository;
import com.naiterui.ehp.bs.b2c.inventory.service.IStockService;
import com.naiterui.ehp.bs.b2c.pharmacy.bo.WarehouseGoodsInventoryBO;
import com.naiterui.ehp.bs.b2c.pharmacy.exception.PharmacyBusinessException;
import com.naiterui.ehp.bs.b2c.pharmacy.service.IWarehouseService;
import com.naiterui.ehp.bs.b2c.product.service.ISkuService;
import com.naiterui.ehp.bs.b2c.thirdparty.service.IPrescriptionPlatformService;
import com.naiterui.ehp.bs.b2c.thirdparty.vo.ProductInventoryParamVO;
import com.naiterui.ehp.bs.b2c.thirdparty.vo.ProductInventoryVO;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 库存业务逻辑
 */
@Slf4j
@Service
public class StockServiceImpl implements IStockService {

    @Autowired
    private IStockRepository stockRepository;
    @Autowired
    private IStockLogRepository stockLogRepository;
    @Autowired
    private IWarehouseService warehouseService;
    @Autowired
    private ISkuService skuService;
    @Autowired
    private IPrescriptionPlatformService platformService;

    /**
     * 订单拒绝redis
     */
    private static final String WAREHOUSE_REJECT_KEY = MallConstants.RDS_KEY_PRE_MALL + "warehouse_reject_key_";

    @Override
    public void remove(Long warehouseId, Set<Long> skuIds) throws StockServiceException {
        List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdIn(warehouseId, skuIds);
        for (Inventory inventory : inventories) {
            if (inventory.getWillQuantity() != 0 || inventory.getTakeUpQuantity() != 0) {
                log.warn("当前sku存在未发货订单 warehouseId:{} skuId:{}", warehouseId, inventory.getSkuId());
                throw new StockServiceException(StockExceptionCodes.REMOVE_STOCK_ERROR);
            }
        }
        this.stockRepository.removeAllByWarehouseIdAndSkuIdIn(warehouseId, skuIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public StockBO getStock(Long skuId) {
        log.info("获取库存数量 请求参数：skuId = {}", skuId);
        List<StockBO> stocks = this.getStocks(Sets.newHashSet(skuId));
        if (CollectionUtil.isEmpty(stocks)) {
            return null;
        }
        return stocks.get(0);
    }

    @Override
    public List<PlatformStockBO> getPlatformStocks(Set<String> numbers) {
        List<PlatformStockBO> result = new ArrayList<>();
        List<ProductInventoryParamVO> drugList = numbers.stream().map(e -> ProductInventoryParamVO.builder().drugCode(e).build())
            .collect(Collectors.toList());
        List<ProductInventoryVO> productInventoryVOS = platformService.getProductInventory(drugList);
        if (CollectionUtil.isNotEmpty(productInventoryVOS)) {
            result = productInventoryVOS.stream()
                .map(e -> PlatformStockBO.builder().number(e.getDrugCode()).inventory(e.getAvailableSumQty().longValue()).build()).collect(
                    Collectors.toList());
        }
        return result;
    }

    @Override
    public List<StockBO> getStocks(final Set<Long> skuIdsPara) {
        HashSet<Long> skuIds = Sets.newHashSet(skuIdsPara);
        log.info("批量获取库存数量 请求参数：skuIds = {}", skuIds);
        List<Inventory> list = this.stockRepository.findBySkuIdInAndStatus(skuIds, Inventory.STATUS_NORMAL);
        List<StockBO> collect = list.stream()
            .map(inventory -> {
                StockBO stock = new StockBO();
                stock.setProductId(inventory.getProductId());
                stock.setSkuId(inventory.getSkuId());
                stock.setRemainQuantity(
                    inventory.getPhysicalQuantity() - inventory.getWillQuantity() - inventory.getTakeUpQuantity());
                return stock;
            })
            .collect(Collectors.toList());

        //计算
        Map<Long, StockBO> collect1 = collect.stream().collect(Collectors.toMap(StockBO::getSkuId, Function.identity(), (o, o2) -> {
            o.setRemainQuantity(o.getRemainQuantity() + o2.getRemainQuantity());
            return o;
        }));
        List<StockBO> stockBOS = Lists.newArrayList(collect1.values());
        Set<Long> collectSkuIds = list.stream().map(Inventory::getSkuId).collect(Collectors.toSet());
        skuIds.removeAll(collectSkuIds);
        if (CollectionUtil.isNotEmpty(skuIds)) {
            List<SkuBO> skuBOS = this.skuService.getListBySkuIds(skuIds);
            Map<Long, Long> productCollect = skuBOS.stream().collect(Collectors.toMap(SkuBO::getSkuId, SkuBO::getProductId));
            skuIds.forEach(skuId -> {
                StockBO stock = new StockBO();
                stock.setProductId(productCollect.get(skuId));
                stock.setSkuId(skuId);
                stock.setRemainQuantity(0L);
                stockBOS.add(stock);
            });
        }

        stockBOS.stream().filter(stockBO -> stockBO.getRemainQuantity() < 0).forEach(stockBO -> {
            stockBO.setRemainQuantity(0L);
            log.warn("商品库存小于0,默认设置为0 {}", stockBO);
        });
        log.info("批量获取库存数量 返回结果：stockBOS = {}", stockBOS);
        return stockBOS;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(@NonNull Long warehouseId, @NonNull Integer status, @NonNull String operator) {
        log.info("更新库存状态 请求参数：warehouseId = {}, status = {}, operator = {}", warehouseId, status, operator);
        List<Inventory> list = this.stockRepository.findByWarehouseId(warehouseId);
        list.forEach(inventory -> {
            inventory.setStatus(status);
            inventory.setChangedAt(new Date());
            inventory.setChangedBy(operator);
        });
        if (CollectionUtil.isNotEmpty(list)) {
            this.stockRepository.update(list);
        }
    }

    /**
     * 用户下单分配库存，扣减欲占库存
     * <p>
     * 1、筛选满足订单中全部商品的合作药店列表。 2、筛选可用库存满足的药店列表。 3、按照系统排序选择最先添加的药店列表。 4、将药店（库房）分配至订单。并进行库存预占。 5、如果订单被某个药店拒绝，那么放到最后
     */
    @NonNull
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long preAssignWarehouse(@NonNull Long orderId, String orderSn, @NonNull List<StockQuantityBO> pqs) throws StockServiceException {
        log.info("用户下单分配库存 请求参数：orderId = {}, orderSn = {}, pqs = {}", orderId, orderSn, pqs);
        Set<Long> skuIds = pqs.stream().map(StockQuantityBO::getSkuId).collect(Collectors.toSet());
        List<Long> warehouseIds = this.warehouseService.getAllWarehouseIds();
        // 被拒绝的药店  如果订单被某个药店拒绝，那么放到最后
        Object rejectWarehouseId = RedisUtil.valueOps().getObject(WAREHOUSE_REJECT_KEY);
        if (rejectWarehouseId != null) {
            warehouseIds.remove((Long) rejectWarehouseId);
            warehouseIds.add((Long) rejectWarehouseId);
        }
        Map<Long, Integer> stockQuantityMap = pqs.stream()
            .collect(Collectors.toMap(StockQuantityBO::getSkuId, StockQuantityBO::getQuantity, Integer::sum));
        for (Long warehouseId : warehouseIds) {
            List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdInAndStatus(warehouseId, skuIds, Inventory.STATUS_NORMAL);
            if (CollectionUtil.isEmpty(inventories)) {
                continue;
            }
            Set<Long> collect = inventories.stream().map(Inventory::getSkuId).collect(Collectors.toSet());
            // 当前仓库没有该订单所有商品
            if (!collect.containsAll(skuIds)) {
                continue;
            }
            try {
                List<InventoryLog> inventoryLogs = Lists.newArrayList();
                for (Inventory inventory : inventories) {
                    Long willQuantity = inventory.getWillQuantity();
                    Long takeUpQuantity = inventory.getTakeUpQuantity();
                    Long physicalQuantity = inventory.getPhysicalQuantity();
                    Long skuId = inventory.getSkuId();
                    Integer quantity = stockQuantityMap.get(skuId);
                    if (willQuantity + takeUpQuantity + quantity > physicalQuantity) {
                        log.warn("当前商品库存不足 orderId:{} inventory:{}", orderId, inventory);
                        throw new StockServiceException(StockExceptionCodes.INSUFFICIENT_INVENTORY, String.format("库存不足 skuId:%s", skuId));
                    }
                    // 增加欲占
                    inventory.setWillQuantity(willQuantity + quantity);
                    inventory.setChangedAt(new Date());
                    inventory.setChangedBy("system");
                    InventoryLog inventoryLog = InventoryLog.builder()
                        .orderId(orderId)
                        .orderSn(orderSn)
                        .warehouseId(warehouseId)
                        .productId(inventory.getProductId())
                        .skuId(inventory.getSkuId())
                        .businessType(InventoryLog.BUSINESS_TYPE_PRE_ASSIGN)
                        .changeType(InventoryLog.CHANGE_TYPE_WILL)
                        .physicalQuantity(inventory.getPhysicalQuantity())
                        .willQuantity(willQuantity)
                        .takeUpQuantity(inventory.getTakeUpQuantity())
                        .quantity(quantity)
                        .createdAt(new Date())
                        .createdBy("system")
                        .changedAt(new Date())
                        .changedBy("system")
                        .build();
                    inventoryLogs.add(inventoryLog);
                }
                this.stockRepository.update(inventories);
                this.stockLogRepository.save(inventoryLogs);
                return warehouseId;
            } catch (StockServiceException e) {
                log.warn("当前仓库不能满足该订单,尝试下一个仓库 orderId:{} warehouseId:{} msg:{}", orderId, warehouseId, e.getMessage());
            }
        }

        throw new StockServiceException(StockExceptionCodes.INSUFFICIENT_INVENTORY);

    }

    @Override
    @Transactional(readOnly = true)
    public boolean checkWarehouseSku(Long warehouseId, List<StockQuantityBO> pqs) {
        Set<Long> skuIds = pqs.stream().map(StockQuantityBO::getSkuId).collect(Collectors.toSet());
        List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdInAndStatus(warehouseId, skuIds, Inventory.STATUS_NORMAL);
        return skuIds.size() == inventories.size();
    }

    /**
     * 订单审核完成后，进行库房分配
     * <p>
     * 1、订单是否已经有指定库房，如果有指定药店，则直接判断库存是否满足，满足则进行库存预占清理（根据订单号清理相关的全部预占记录），并将库存占用。如不满足则分仓失败。 2、库房无指定库房（订单拒绝且未人工指定库房），则按照预分配逻辑进行订单分配并进行库存占用。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignWarehouse(Long orderId, String orderSn, Long warehouseId, List<StockQuantityBO> pqs) throws StockServiceException {
        log.info("订单进行库房分配 请求参数：orderId = {}, orderSn = {}, warehouseId = {}, pqs = {}", orderId, orderSn, warehouseId, pqs);
        //TODO 2019-08-08 15:27 王泽浩 如果仓库呗冻结怎么办？
        List<InventoryLog> inventoryLogs = Lists.newArrayList();
        Set<Long> skuIds = pqs.stream().map(StockQuantityBO::getSkuId).collect(Collectors.toSet());
        List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdInAndStatus(warehouseId, skuIds, Inventory.STATUS_NORMAL);

        Set<Long> collect = inventories.stream().map(Inventory::getSkuId).collect(Collectors.toSet());
        if (!collect.containsAll(skuIds)) {
            log.info("当前仓库可能存在下架商品，订单无法进行库房分配。orderId:{} warehouseId:{} 不存的商品skuId:{}", orderId, warehouseId, skuIds.removeAll(collect));
            throw new StockServiceException(StockExceptionCodes.ORDER_GOODS_CANT_FULLY_ASSIGNED_TO_PHARMACY);
        }

        Map<Long, Integer> stockQuantityMap = pqs.stream()
            .collect(Collectors.toMap(StockQuantityBO::getSkuId, StockQuantityBO::getQuantity, Integer::sum));
        ;
        for (Inventory inventory : inventories) {
            Long willQuantity = inventory.getWillQuantity();
            Long takeUpQuantity = inventory.getTakeUpQuantity();
            Long physicalQuantity = inventory.getPhysicalQuantity();
            Long skuId = inventory.getSkuId();

            if (willQuantity + takeUpQuantity > physicalQuantity) {
                log.warn("当前商品库存不足 orderId:{} inventory:{}", orderId, inventory);
                throw new StockServiceException(StockExceptionCodes.INSUFFICIENT_INVENTORY);
            }
            Integer quantity = stockQuantityMap.get(skuId);
            //清空欲占
            inventory.setWillQuantity(willQuantity - quantity);
            //增加欲占
            inventory.setTakeUpQuantity(takeUpQuantity + quantity);
            inventory.setChangedAt(new Date());
            inventory.setChangedBy("system");
            InventoryLog inventoryLog = InventoryLog.builder()
                .orderId(orderId)
                .orderSn(orderSn)
                .warehouseId(warehouseId)
                .productId(inventory.getProductId())
                .skuId(skuId)
                .businessType(InventoryLog.BUSINESS_TYPE_ASSIGN)
                .changeType(InventoryLog.CHANGE_TYPE_TAKE_UP)
                .physicalQuantity(physicalQuantity)
                .willQuantity(willQuantity)
                .takeUpQuantity(takeUpQuantity)
                .quantity(quantity)
                .createdAt(new Date())
                .createdBy("system")
                .changedAt(new Date())
                .changedBy("system")
                .build();
            inventoryLogs.add(inventoryLog);

        }
        if (CollectionUtil.isNotEmpty(inventories)) {
            this.stockRepository.update(inventories);
            this.stockLogRepository.saveAll(inventoryLogs);
        }
    }

    @Override
    public void warehouseReject(Long orderId, String orderSn, Long warehouseId, List<StockQuantityBO> pqs) {
        log.info("清理占用 请求参数：orderId = {}, orderSn = {}, warehouseId = {}, pqs = {}", orderId, orderSn, warehouseId, pqs);
        RedisUtil.valueOps().set(WAREHOUSE_REJECT_KEY, warehouseId);
        Set<Long> skuIds = pqs.stream().map(StockQuantityBO::getSkuId).collect(Collectors.toSet());
        List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdIn(warehouseId, skuIds);
        Set<Long> collect = inventories.stream().map(Inventory::getSkuId).collect(Collectors.toSet());
        if (!collect.containsAll(skuIds)) {
            log.info("当前仓库可能存在下架商品，不处理对应商品欲占。orderId:{} warehouseId:{} 不存的商品skuId:{}", orderId, warehouseId, skuIds.removeAll(collect));
        }
        List<InventoryLog> inventoryLogs = Lists.newArrayList();
        Map<Long, Integer> stockQuantityMap = pqs.stream()
            .collect(Collectors.toMap(StockQuantityBO::getSkuId, StockQuantityBO::getQuantity, Integer::sum));
        ;
        for (Inventory inventory : inventories) {
            Long willQuantity = inventory.getWillQuantity();
            Long takeUpQuantity = inventory.getTakeUpQuantity();
            Long physicalQuantity = inventory.getPhysicalQuantity();
            Long skuId = inventory.getSkuId();
            inventory.setWillQuantity(willQuantity);
            Integer quantity = stockQuantityMap.get(skuId);
            inventory.setTakeUpQuantity(takeUpQuantity - quantity);
            inventory.setPhysicalQuantity(physicalQuantity);
            inventory.setChangedAt(new Date());
            inventory.setChangedBy("system");
            InventoryLog inventoryLog = InventoryLog.builder()
                .orderId(orderId)
                .orderSn(orderSn)
                .warehouseId(warehouseId)
                .productId(inventory.getProductId())
                .skuId(skuId)
                .businessType(InventoryLog.BUSINESS_TYPE_REJECT)
                .changeType(InventoryLog.CHANGE_TYPE_SUB_TAKE_UP)
                .physicalQuantity(physicalQuantity)
                .willQuantity(willQuantity)
                .takeUpQuantity(takeUpQuantity)
                .quantity(quantity)
                .createdAt(new Date())
                .createdBy("system")
                .changedAt(new Date())
                .changedBy("system")
                .build();
            inventoryLogs.add(inventoryLog);
        }
        if (CollectionUtil.isNotEmpty(inventories)) {
            this.stockRepository.update(inventories);
            this.stockLogRepository.saveAll(inventoryLogs);
        }
    }

    @Override
    public void modifyWarehouse(Long orderId, String orderSn, Long warehouseId, Long modifyWarehouseId,
        List<StockQuantityBO> pqs) throws StockServiceException {
        log.info("修改仓库 请求参数：orderId = {}, orderSn = {}, warehouseId = {}, modifyWarehouseId = {}, pqs = {}", orderId, orderSn, warehouseId,
            modifyWarehouseId, pqs);

        //没有修改
        if (warehouseId == null && modifyWarehouseId == null) {
            log.info("该订单没有选择仓库，不修改库存。orderId:{}", orderId);
            return;
        }
        if (Objects.equals(warehouseId, modifyWarehouseId)) {
            log.info("该订单没有变更仓库，不修改库存。orderId:{} warehouseId:{}", orderId, warehouseId);
            return;
        }
        Set<Long> skuIds = pqs.stream().map(StockQuantityBO::getSkuId).collect(Collectors.toSet());
        if (warehouseId != null) {
            List<InventoryLog> inventoryLogs = Lists.newArrayList();
            // 清空欲占
            List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdIn(warehouseId, skuIds);
            Map<Long, Integer> stockQuantityMap = pqs.stream()
                .collect(
                    Collectors.toMap(StockQuantityBO::getSkuId, StockQuantityBO::getQuantity, Integer::sum));
            ;
            for (Inventory inventory : inventories) {
                Long willQuantity = inventory.getWillQuantity();
                Long takeUpQuantity = inventory.getTakeUpQuantity();
                Long physicalQuantity = inventory.getPhysicalQuantity();
                Long skuId = inventory.getSkuId();
                Integer quantity = stockQuantityMap.get(skuId);
                inventory.setWillQuantity(willQuantity - quantity);
                inventory.setTakeUpQuantity(takeUpQuantity);
                inventory.setPhysicalQuantity(physicalQuantity);
                inventory.setChangedAt(new Date());
                inventory.setChangedBy("system");
                InventoryLog inventoryLog = InventoryLog.builder()
                    .orderId(orderId)
                    .orderSn(orderSn)
                    .warehouseId(warehouseId)
                    .productId(inventory.getProductId())
                    .skuId(skuId)
                    .businessType(InventoryLog.BUSINESS_TYPE_MODIFY_WAREHOUSE)
                    .changeType(InventoryLog.CHANGE_TYPE_REJECT)
                    .physicalQuantity(physicalQuantity)
                    .willQuantity(willQuantity)
                    .takeUpQuantity(takeUpQuantity)
                    .quantity(quantity)
                    .createdAt(new Date())
                    .createdBy("system")
                    .changedAt(new Date())
                    .changedBy("system")
                    .build();
                inventoryLogs.add(inventoryLog);

            }
            if (CollectionUtil.isNotEmpty(inventories)) {
                this.stockRepository.update(inventories);
                this.stockLogRepository.saveAll(inventoryLogs);
            }
        }

        if (modifyWarehouseId != null) {
            try {
                this.warehouseService.checkStatusNormal(modifyWarehouseId);
            } catch (PharmacyBusinessException e) {
                log.info("当前仓库已经停用，无法欲占库存。orderId:{} warehouseId:{}", orderId, warehouseId);
                throw new StockServiceException(StockExceptionCodes.ORDER_GOODS_CANT_FULLY_ASSIGNED_TO_PHARMACY);
            }
            // 增加欲占
            List<Inventory> inventories = this.stockRepository
                .findByWarehouseIdAndSkuIdInAndStatus(modifyWarehouseId, skuIds, Inventory.STATUS_NORMAL);
            Set<Long> collect = inventories.stream().map(Inventory::getSkuId).collect(Collectors.toSet());
            if (!collect.containsAll(skuIds)) {
                log.info("当前仓库可能存在下架商品，无法欲占库存。orderId:{} warehouseId:{} 不存的商品skuId:{}", orderId, warehouseId, skuIds.removeAll(collect));
                throw new StockServiceException(StockExceptionCodes.ORDER_GOODS_CANT_FULLY_ASSIGNED_TO_PHARMACY);
            }
            List<InventoryLog> inventoryLogs = Lists.newArrayList();
            Map<Long, Integer> stockQuantityMap = pqs.stream()
                .collect(
                    Collectors.toMap(StockQuantityBO::getSkuId, StockQuantityBO::getQuantity, Integer::sum));
            ;
            for (Inventory inventory : inventories) {
                Long willQuantity = inventory.getWillQuantity();
                Long takeUpQuantity = inventory.getTakeUpQuantity();
                Long physicalQuantity = inventory.getPhysicalQuantity();
                Long skuId = inventory.getSkuId();
                Integer quantity = stockQuantityMap.get(skuId);
                if (willQuantity + takeUpQuantity + quantity >= physicalQuantity) {
                    log.warn("当前商品库存不足 orderId:{} inventory:{}", orderId, inventory);
                    throw new StockServiceException(StockExceptionCodes.INSUFFICIENT_INVENTORY);
                }
                inventory.setWillQuantity(willQuantity + quantity);
                inventory.setTakeUpQuantity(takeUpQuantity);
                inventory.setPhysicalQuantity(physicalQuantity);
                inventory.setChangedAt(new Date());
                inventory.setChangedBy("system");
                InventoryLog inventoryLog = InventoryLog.builder()
                    .orderId(orderId)
                    .orderSn(orderSn)
                    .warehouseId(warehouseId)
                    .productId(inventory.getProductId())
                    .skuId(skuId)
                    .businessType(InventoryLog.BUSINESS_TYPE_MODIFY_WAREHOUSE)
                    .changeType(InventoryLog.CHANGE_TYPE_WILL)
                    .physicalQuantity(physicalQuantity)
                    .willQuantity(willQuantity)
                    .takeUpQuantity(takeUpQuantity)
                    .quantity(quantity)
                    .createdAt(new Date())
                    .createdBy("system")
                    .changedAt(new Date())
                    .changedBy("system")
                    .build();
                inventoryLogs.add(inventoryLog);
            }
            if (CollectionUtil.isNotEmpty(inventories)) {
                this.stockRepository.update(inventories);
                this.stockLogRepository.saveAll(inventoryLogs);
            }
        }

    }

    @Override
    public void cancel(Long orderId, String orderSn, Long warehouseId, int type, List<StockQuantityBO> pqs) {
        log.info("清理欲占/占用 请求参数：orderId = {}, orderSn = {}, warehouseId = {}, pqs = {}", orderId, orderSn, warehouseId, pqs);
        Set<Long> skuIds = pqs.stream().map(StockQuantityBO::getSkuId).collect(Collectors.toSet());
        List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdIn(warehouseId, skuIds);
        Set<Long> collect = inventories.stream().map(Inventory::getSkuId).collect(Collectors.toSet());
        if (!collect.containsAll(skuIds)) {
            log.info("当前仓库可能存在下架商品，不会处理该库存。orderId:{} warehouseId:{} 不存的商品skuId:{}", orderId, warehouseId, skuIds.removeAll(collect));
        }
        List<InventoryLog> inventoryLogs = Lists.newArrayList();
        Map<Long, Integer> stockQuantityMap = pqs.stream()
            .collect(Collectors.toMap(StockQuantityBO::getSkuId, StockQuantityBO::getQuantity, Integer::sum));
        ;
        for (Inventory inventory : inventories) {
            Long willQuantity = inventory.getWillQuantity();
            Long takeUpQuantity = inventory.getTakeUpQuantity();
            Long physicalQuantity = inventory.getPhysicalQuantity();
            Long skuId = inventory.getSkuId();
            Integer quantity = stockQuantityMap.get(skuId);
            if (type == 0) {
                inventory.setWillQuantity(willQuantity - quantity);
                inventory.setTakeUpQuantity(takeUpQuantity);
            } else {
                inventory.setWillQuantity(willQuantity);
                inventory.setTakeUpQuantity(takeUpQuantity - quantity);
            }
            inventory.setPhysicalQuantity(physicalQuantity);
            inventory.setChangedAt(new Date());
            inventory.setChangedBy("system");
            InventoryLog inventoryLog = InventoryLog.builder()
                .orderId(orderId)
                .orderSn(orderSn)
                .warehouseId(warehouseId)
                .productId(inventory.getProductId())
                .skuId(skuId)
                .businessType(InventoryLog.BUSINESS_TYPE_CANCEL)
                .changeType(type == 0 ? InventoryLog.CHANGE_TYPE_REJECT : InventoryLog.CHANGE_TYPE_SUB_TAKE_UP)
                .physicalQuantity(physicalQuantity)
                .willQuantity(willQuantity)
                .takeUpQuantity(takeUpQuantity)
                .quantity(quantity)
                .createdAt(new Date())
                .createdBy("system")
                .changedAt(new Date())
                .changedBy("system")
                .build();
            inventoryLogs.add(inventoryLog);

        }
        if (CollectionUtil.isNotEmpty(inventories)) {
            this.stockRepository.update(inventories);
            this.stockLogRepository.saveAll(inventoryLogs);
        }
    }

    @Override
    public void returnAudit(Long orderId, String orderSn, Long warehouseId, int type, List<StockQuantityBO> pqs, String operator) {
        log.info("订单反审，清理欲占/占用 请求参数：orderId = {}, orderSn = {}, warehouseId = {} type = {}, pqs = {} operator = {}", orderId, orderSn, warehouseId,
            type, pqs, operator);
        Set<Long> skuIds = pqs.stream().map(StockQuantityBO::getSkuId).collect(Collectors.toSet());
        List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdIn(warehouseId, skuIds);
        Set<Long> collect = inventories.stream().map(Inventory::getSkuId).collect(Collectors.toSet());
        if (!collect.containsAll(skuIds)) {
            log.info("当前仓库可能存在下架商品，不会处理该库存。orderId:{} warehouseId:{} 不存的商品skuId:{}", orderId, warehouseId, skuIds.removeAll(collect));
        }
        List<InventoryLog> inventoryLogs = Lists.newArrayList();
        Map<Long, Integer> stockQuantityMap = pqs.stream()
            .collect(Collectors.toMap(StockQuantityBO::getSkuId, StockQuantityBO::getQuantity, Integer::sum));
        ;
        for (Inventory inventory : inventories) {
            Long willQuantity = inventory.getWillQuantity();
            Long takeUpQuantity = inventory.getTakeUpQuantity();
            Long physicalQuantity = inventory.getPhysicalQuantity();
            Long skuId = inventory.getSkuId();
            Integer quantity = stockQuantityMap.get(skuId);
            if (type == 0) {
                inventory.setWillQuantity(willQuantity - quantity);
            } else {
                inventory.setTakeUpQuantity(takeUpQuantity - quantity);
            }
            inventory.setPhysicalQuantity(physicalQuantity);
            inventory.setChangedAt(new Date());
            inventory.setChangedBy(operator);
            InventoryLog inventoryLog = InventoryLog.builder()
                .orderId(orderId)
                .orderSn(orderSn)
                .warehouseId(warehouseId)
                .productId(inventory.getProductId())
                .skuId(skuId)
                .businessType(InventoryLog.BUSINESS_TYPE_RETURN_AUDIT)
                .changeType(type == 0 ? InventoryLog.CHANGE_TYPE_REJECT : InventoryLog.CHANGE_TYPE_SUB_TAKE_UP)
                .physicalQuantity(physicalQuantity)
                .willQuantity(willQuantity)
                .takeUpQuantity(takeUpQuantity)
                .quantity(quantity)
                .createdAt(new Date())
                .createdBy(operator)
                .changedAt(new Date())
                .changedBy(operator)
                .build();
            inventoryLogs.add(inventoryLog);

        }
        if (CollectionUtil.isNotEmpty(inventories)) {
            this.stockRepository.update(inventories);
            this.stockLogRepository.saveAll(inventoryLogs);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deliveryOrder(Long orderId, String orderSn, Long warehouseId, List<StockQuantityBO> pqs) throws StockServiceException {
        log.info("订单发货 请求参数：orderId = {}, orderSn = {}, warehouseId = {}, pqs = {}", orderId, orderSn, warehouseId, pqs);
        Set<Long> skuIds = pqs.stream().map(StockQuantityBO::getSkuId).collect(Collectors.toSet());
        List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdInAndStatus(warehouseId, skuIds, Inventory.STATUS_NORMAL);
        Set<Long> collect = inventories.stream().map(Inventory::getSkuId).collect(Collectors.toSet());
        if (!collect.containsAll(skuIds)) {
            log.info("当前仓库可能存在下架商品，不能发货。orderId:{} warehouseId:{} 不存的商品skuId:{}", orderId, warehouseId, skuIds.removeAll(collect));
            throw new StockServiceException(StockExceptionCodes.ORDER_GOODS_CANT_FULLY_ASSIGNED_TO_PHARMACY);
        }
        List<InventoryLog> inventoryLogs = Lists.newArrayList();
        Map<Long, Integer> stockQuantityMap = pqs.stream()
            .collect(Collectors.toMap(StockQuantityBO::getSkuId, StockQuantityBO::getQuantity, Integer::sum));
        ;
        for (Inventory inventory : inventories) {
            Long willQuantity = inventory.getWillQuantity();
            Long takeUpQuantity = inventory.getTakeUpQuantity();
            Long physicalQuantity = inventory.getPhysicalQuantity();
            Long skuId = inventory.getSkuId();
            if (willQuantity + takeUpQuantity >= physicalQuantity) {
                log.warn("当前商品库存不足 orderId:{} inventory:{}", orderId, inventory);
            }
            Integer quantity = stockQuantityMap.get(skuId);
            inventory.setWillQuantity(willQuantity);
            //清空占用
            inventory.setTakeUpQuantity(takeUpQuantity - quantity);
            //扣减实物库存
            inventory.setPhysicalQuantity(physicalQuantity - quantity);
            inventory.setChangedAt(new Date());
            inventory.setChangedBy("system");
            InventoryLog inventoryLog = InventoryLog.builder()
                .orderId(orderId)
                .orderSn(orderSn)
                .warehouseId(warehouseId)
                .productId(inventory.getProductId())
                .skuId(skuId)
                .businessType(InventoryLog.BUSINESS_TYPE_DELIVERY)
                .changeType(InventoryLog.CHANGE_TYPE_PHYSICAL)
                .physicalQuantity(physicalQuantity)
                .willQuantity(willQuantity)
                .takeUpQuantity(takeUpQuantity)
                .quantity(quantity)
                .createdAt(new Date())
                .createdBy("system")
                .changedAt(new Date())
                .changedBy("system")
                .build();
            inventoryLogs.add(inventoryLog);
        }
        if (CollectionUtil.isNotEmpty(inventories)) {
            this.stockRepository.update(inventories);
            this.stockLogRepository.saveAll(inventoryLogs);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInventory(Long warehouseId, Set<WarehouseGoodsInventoryBO> warehouseGoodsInventorys,
        String operator) throws StockServiceException {
        Set<Long> skuIds = warehouseGoodsInventorys.stream().map(WarehouseGoodsInventoryBO::getSkuId).collect(Collectors.toSet());
        Map<Long, Long> skuIdInventoryMap = warehouseGoodsInventorys.stream()
            .collect(Collectors.toMap(WarehouseGoodsInventoryBO::getSkuId,
                WarehouseGoodsInventoryBO::getQuantity));
        List<InventoryLog> inventoryLogs = Lists.newArrayList();
        List<Inventory> inventories = this.stockRepository.findByWarehouseIdAndSkuIdInAndStatus(warehouseId, skuIds, Inventory.STATUS_NORMAL);
        for (Inventory inventory : inventories) {
            Long skuId = inventory.getSkuId();
            Long modifyPhysicalQuantity = skuIdInventoryMap.get(skuId);
            log.info("设置库存 warehouseId:{} skuId:{} physicalQuantity:{}", warehouseId, skuId, modifyPhysicalQuantity);
            skuIds.remove(skuId);
            Long willQuantity = inventory.getWillQuantity();
            Long takeUpQuantity = inventory.getTakeUpQuantity();
            if (willQuantity + takeUpQuantity > modifyPhysicalQuantity) {
                log.error("当前设置库存小于欲占+占用，不能设置。inventory:{} 欲修改后实物库存:{}", inventory, modifyPhysicalQuantity);
                throw new StockServiceException(StockExceptionCodes.MODIFY_STOCK_ERROR);
            }
            Long physicalQuantity = inventory.getPhysicalQuantity();

            inventory.setPhysicalQuantity(modifyPhysicalQuantity);
            inventory.setChangedAt(new Date());
            inventory.setChangedBy(operator);

            InventoryLog inventoryLog = InventoryLog.builder()
                .warehouseId(warehouseId)
                .productId(inventory.getProductId())
                .skuId(skuId)
                .businessType(InventoryLog.BUSINESS_TYPE_MODIFY)
                .changeType(InventoryLog.CHANGE_TYPE_MODIFY_PHYSICAL)
                .physicalQuantity(physicalQuantity)
                .willQuantity(willQuantity)
                .takeUpQuantity(takeUpQuantity)
                .quantity(inventory.getPhysicalQuantity().intValue())
                .createdAt(new Date())
                .createdBy(operator)
                .changedAt(new Date())
                .changedBy(operator)
                .build();
            inventoryLogs.add(inventoryLog);
        }
        if (CollectionUtil.isNotEmpty(inventories)) {
            this.stockRepository.update(inventories);
        }

        List<SkuBO> skuList = this.skuService.getListBySkuIds(skuIds);
        Map<Long, Long> collect = skuList.stream().collect(Collectors.toMap(SkuBO::getSkuId, SkuBO::getProductId));
        for (Long skuId : skuIds) {
            Long physicalQuantity = skuIdInventoryMap.get(skuId);
            log.info("设置库存 warehouseId:{} skuId:{} physicalQuantity:{}", warehouseId, skuId, physicalQuantity);
            Inventory inventory = new Inventory();
            inventory.setWarehouseId(warehouseId);
            inventory.setProductId(collect.get(skuId));
            inventory.setSkuId(skuId);
            inventory.setStatus(Inventory.STATUS_NORMAL);
            inventory.setPhysicalQuantity(physicalQuantity);
            inventory.setWillQuantity(0L);
            inventory.setTakeUpQuantity(0L);
            inventory.setCreatedAt(new Date());
            inventory.setCreatedBy(operator);
            inventory.setChangedAt(new Date());
            inventory.setChangedBy(operator);
            InventoryLog inventoryLog = InventoryLog.builder()
                .warehouseId(warehouseId)
                .productId(inventory.getProductId())
                .skuId(skuId)
                .businessType(InventoryLog.BUSINESS_TYPE_MODIFY)
                .changeType(InventoryLog.CHANGE_TYPE_MODIFY_PHYSICAL)
                .physicalQuantity(0L)
                .willQuantity(0L)
                .takeUpQuantity(0L)
                .quantity(0)
                .createdAt(new Date())
                .createdBy(operator)
                .changedAt(new Date())
                .changedBy(operator)
                .build();
            inventoryLogs.add(inventoryLog);
            this.stockRepository.save(inventory);
        }
        this.stockLogRepository.saveAll(inventoryLogs);
    }

}
